package com.bg7yoz.ft8cn.ft8signal;
/**
 * 按照FT8协议打包符号。
 * @author BGY70Z
 * @date 2023-03-20
 */

import android.util.Log;

import com.bg7yoz.ft8cn.Ft8Message;
import com.bg7yoz.ft8cn.ft8transmit.GenerateFT8;

public class FT8Package {
    private static final String TAG = "FT8Package";
    public static final int NTOKENS = 2063592;
    public static final int MAX22 = 4194304;
    public static final int MAXGRID4 = 32400;


    private static final String A1 = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final String A2 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final String A3 = "0123456789";
    private static final String A4 = " ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final String A5 = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/";

    static {
        System.loadLibrary("ft8cn");
    }


    public static byte[] generatePack77_i4(Ft8Message message) {

        String toCall = message.callsignTo.replace("<", "").replace(">", "");
        String fromCall = message.callsignFrom.replace("<", "").replace(">", "");
        int hash12;
        if (message.checkIsCQ()) {//如果是CQ，就把自己的呼号的哈希加上
            hash12 = getHash12(fromCall);
        } else {
            hash12 = getHash12(toCall);
        }
        if (fromCall.length() > 11) {//非标准呼号的长度不等长于11位
            fromCall = fromCall.substring(0, 11);
        }

        byte[] data = new byte[10];
        long n58 = 0;
        for (int i = 0; i < fromCall.length(); i++) {
            n58 = n58 * 38 + A5.indexOf(fromCall.charAt(i));
        }
        //n58=3479529522318088L;

        data[0] = (byte) ((hash12 & 0x00000fff) >> 4);
        data[1] = (byte) ((hash12 & 0x0000000f) << 4);
        data[1] = (byte) (data[1] | ((n58 & 0x0fff_ffff_ffff_ffffL) >> 54));
        data[2] = (byte) (((n58 & 0x00ff_ffff_ffff_ffffL) >> 54 - 8));
        data[3] = (byte) (((n58 & 0x0000_ffff_ffff_ffffL) >> 54 - 8 - 8));
        data[4] = (byte) (((n58 & 0x0000_00ff_ffff_ffffL) >> 54 - 8 - 8 - 8));
        data[5] = (byte) (((n58 & 0x0000_0000_ffff_ffffL) >> 54 - 8 - 8 - 8 - 8));
        data[6] = (byte) (((n58 & 0x0000_0000_00ff_ffffL) >> 54 - 8 - 8 - 8 - 8 - 8));
        data[7] = (byte) (((n58 & 0x0000_0000_0000_ffffL) >> 54 - 48));
        data[8] = (byte) (((n58 & 0x0000_0000_0000_00ffL) << 2));
        //RRR=1,RR73=2,73=3,""=0
        if (message.checkIsCQ()) {
            //data[8]=(byte) (data[8]| );//h1=0,r2=0 i3-4
            data[9] = (byte) 0x60;
        } else {
            //data[9]=(byte)(data[9]&0xbf);//h1=0;
            data[9] = (byte) 0x20;
            int r2;
            if (message.extraInfo.equals("RRR")) {//r2=1
                data[8] = (byte) (data[8] & 0xfe);
                data[9] = (byte) (data[9] | 0x80);
                r2 = 1;
            } else if (message.extraInfo.equals("RR73")) {//r2=2
                data[8] = (byte) (data[8] | 0x01);
                data[9] = (byte) (data[9] | 0x80);
                r2 = 2;
            } else if (message.extraInfo.equals("73")) {//r2=3
                data[8] = (byte) (data[8] | 0x01);
                data[9] = (byte) (data[9] | 0x80);
                r2 = 3;
            }
        }

        return data;
    }


    /**
     * i1=1,i1=2，在FT8协议的定义当中，分别是标准消息，和欧盟甚高频（EU VHF），这两个消息的唯一区别是：
     * i1=1，消息可以带/R，i1=2，消息是可以带/P
     * 所以，这两个消息可以合并为一个类型。
     *
     * @param message 原始消息
     * @return
     */
    public static byte[] generatePack77_i1(Ft8Message message) {
        //Log.e(TAG, "generatePack77_i1: "+message.toString() );
        Log.e(TAG, "generatePack77_i1: message.callsignTo " + message.callsignTo);
        Log.e(TAG, "generatePack77_i1: message.callsignFrom " + message.callsignFrom);
        String toCall = message.callsignTo.replace("<", "").replace(">", "");
        String fromCall = message.callsignFrom.replace("<", "").replace(">", "");

        if (message.checkIsCQ()&& message.modifier!=null){//把修饰符加上
            if (message.modifier.length()>0) {
                toCall = toCall + " " + message.modifier;
            }
        }

        //如果以/P 或/R结尾的呼号，要把这个/P /R去掉
        if (toCall.endsWith("/P") || toCall.endsWith("/R")) {
            toCall = toCall.substring(0, toCall.length() - 2);
        }

        if (fromCall.endsWith("/P") || fromCall.endsWith("/R")) {
            fromCall = message.callsignFrom.substring(0, message.callsignFrom.length() - 2);
        }

        //当双方都是复合呼号或非标准呼号时（带/的呼号），我的呼号变成标准呼号
        if ((toCall.contains("/")) && fromCall.contains("/")) {
            fromCall = fromCall.substring(0, fromCall.indexOf("/"));
        }


        byte[] data = new byte[12];
        data[0] = (byte) ((pack_c28(toCall) & 0x0fffffff) >> 20);
        data[1] = (byte) ((pack_c28(toCall) & 0x00ffffff) >> 12);
        data[2] = (byte) ((pack_c28(toCall) & 0x0000ffff) >> 4);
        data[3] = (byte) ((pack_c28(toCall) & 0x0000000f) << 4);
        data[3] = (byte) (data[3] | (pack_r1_p1(message.callsignTo) << 3));
        data[3] = (byte) (data[3] | (pack_c28(fromCall) & 0x00fffffff) >> 25);


        data[4] = (byte) ((pack_c28(fromCall) & 0x003ffffff) >> 25 - 8);
        data[5] = (byte) ((pack_c28(fromCall) & 0x00003ffff) >> 25 - 8 - 8);
        data[6] = (byte) ((pack_c28(fromCall) & 0x0000003ff) >> 25 - 8 - 8 - 8);
        data[7] = (byte) ((pack_c28(fromCall) & 0x0000000ff) << 7);


        data[7] = (byte) (data[7] | (pack_r1_p1(message.getCallsignFrom())) << 6);
        data[7] = (byte) (data[7] | (pack_R1_g15(message.extraInfo) & 0x0ffff) >> 10);
        data[8] = (byte) ((pack_R1_g15(message.extraInfo) & 0x0003fff) >> 2);
        data[9] = (byte) ((pack_R1_g15(message.extraInfo) & 0x00000ff) << 6);
        data[9] = (byte) (data[9] | (message.i3 & 0x3) << 3);
        return data;
    }

    /**
     * 生成R1+g15数据（网格、或信号报告），实际是16位，包括前面R1。如R-17:R1=1,-17:R1=0
     *
     * @param grid4 网格或信号报告
     * @return 返回R1+g15数据
     */
    public static int pack_R1_g15(String grid4) {
        if (grid4 == null)// 只有两个呼号，没有信号报告和网格
        {
            return MAXGRID4 + 1;
        }
        if (grid4.length() == 0) {// 只有两个呼号，没有信号报告和网格
            return MAXGRID4 + 1;
        }

        // 特殊的报告，RRR,RR73,73
        if (grid4.equals("RRR"))
            return MAXGRID4 + 2;
        if (grid4.equals("RR73"))
            return MAXGRID4 + 3;
        if (grid4.equals("73"))
            return MAXGRID4 + 4;


        // 检查是不是标准的4字符网格
        if (grid4.matches("[A-Z][A-Z][0-9][0-9]")) {
            int igrid4 = grid4.charAt(0) - 'A';
            igrid4 = igrid4 * 18 + (grid4.charAt(1) - 'A');
            igrid4 = igrid4 * 10 + (grid4.charAt(2) - '0');
            igrid4 = igrid4 * 10 + (grid4.charAt(3) - '0');
            return igrid4;
        }


        // 检查是不是信号报告: +dd / -dd / R+dd / R-dd
        // 信号报告在-30到99dB之间
        // 信号报告的正则：[R]?[+-][0-9]{1,2}
        String s = grid4;
        if (grid4.charAt(0) == 'R') {
            s = grid4.substring(1);
            int irpt = 35 + Integer.parseInt(s);
            return (MAXGRID4 + irpt) | 0x8000; // R1 = 1
        } else {
            int irpt = 35 + Integer.parseInt(grid4);
            return (MAXGRID4 + irpt); // R1 = 0
        }

    }

    public static byte pack_r1_p1(String callsign) {
        String s = callsign.substring(callsign.length() - 2);
        if (s.equals("/R") || s.equals("/P")) {
            return 1;
        } else {
            return 0;
        }
    }

    /**
     * 根据呼号生成c28数据。呼号为标准呼号时,不带/R或/P。如果呼号不是标准呼号，用hash22+2063592;
     *
     * @param callsign 呼号
     * @return c28数据
     */
    public static int pack_c28(String callsign) {
        //byte[] data=new byte[]{(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00};
        switch (callsign) {
            case "DE":
                return 0;
            case "QRZ":
                return 1;
            case "CQ":
                return 2;
        }

        //判断是否有修饰符000-999,A-Z,AA-ZZ,AAA-ZZZ,AAAA-ZZZZ
        if (callsign.startsWith("CQ ")&&callsign.length()>3){
            String temp=callsign.substring(3).trim().toUpperCase();
            if (temp.matches("[0-9]{3}")){
                int i=Integer.parseInt(temp);
                return i+3;
            }
            if (temp.matches("[A-Z]{1,4}")) {

                int a0 = 0;
                int a1 = 0;
                int a2 = 0;
                int a3 = 0;
                if (temp.length() == 1) {//A-Z
                    a0= (int) temp.charAt(0) - 65;
                    return a0+1004;
                }
                if (temp.length() == 2) {//AA-ZZ
                    a0 =  (int) temp.charAt(0) - 65;
                    a1 =  (int) temp.charAt(1) - 65;
                    return a0*27+a1+1031;
                }
                if (temp.length() == 3) {//AAA-ZZZ
                    a0 =  (int) temp.charAt(0) - 65;
                    a1 =  (int) temp.charAt(1) - 65;
                    a2 =  (int) temp.charAt(2) - 65;
                    return a0*27*27+a1*27+a2+1760;
                }
                if (temp.length() == 4) {//AAAA-ZZZZ
                    a0 =  (int) temp.charAt(0) - 65;
                    a1 =  (int) temp.charAt(1) - 65;
                    a2 =  (int) temp.charAt(2) - 65;
                    a3 =  (int) temp.charAt(3) - 65;
                    return a0*27*27*27+a1*27*27+a2*27+a3+21443;
                }
            }
        }


        //格式化成标准的呼号。6位、第3位带数字
        //c6也可以是非标准呼号。大于6位的都是非标准呼号
        String c6 = formatCallsign(callsign);
        //if (c6.length()>6){//生成HASH22+2063592
        if (!GenerateFT8.checkIsStandardCallsign(callsign)) {//生成HASH22+2063592
            return NTOKENS + getHash22(callsign);
        }

        //6位呼号取值
        int i0, i1, i2, i3, i4, i5;
        i0 = A1.indexOf(c6.substring(0, 1));
        i1 = A2.indexOf(c6.substring(1, 2));
        i2 = A3.indexOf(c6.substring(2, 3));
        i3 = A4.indexOf(c6.substring(3, 4));
        i4 = A4.indexOf(c6.substring(4, 5));
        i5 = A4.indexOf(c6.substring(5, 6));

        int n28 = i0;
        n28 = n28 * 36 + i1;
        n28 = n28 * 10 + i2;
        n28 = n28 * 27 + i3;
        n28 = n28 * 27 + i4;
        n28 = n28 * 27 + i5;


        return NTOKENS + MAX22 + n28;

    }

    public static long pack_c58(String callsign) {
        //byte[] data=new byte[]{(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00};
        switch (callsign) {
            case "DE":
                return 0;
            case "QRZ":
                return 1;
            case "CQ":
                return 2;
        }

        //格式化成标准的呼号。6位、第3位带数字
        String c6 = formatCallsign(callsign);

//        n58 = ((uint64_t) (a77[1] & 0x0F) << 54); //57 ~ 54 : 4
//        n58 |= ((uint64_t) a77[2] << 46); //53 ~ 46 : 12
//        n58 |= ((uint64_t) a77[3] << 38); //45 ~ 38 : 12
//        n58 |= ((uint64_t) a77[4] << 30); //37 ~ 30 : 12
//        n58 |= ((uint64_t) a77[5] << 22); //29 ~ 22 : 12
//        n58 |= ((uint64_t) a77[6] << 14); //21 ~ 14 : 12
//        n58 |= ((uint64_t) a77[7] << 6); //13 ~ 6 : 12
//        n58 |= ((uint64_t) a77[8] >> 2); //5 ~ 0 : 765432 10


        //6位呼号取值
        int i0, i1, i2, i3, i4, i5;
        i0 = A1.indexOf(c6.substring(0, 1));
        i1 = A2.indexOf(c6.substring(1, 2));
        i2 = A3.indexOf(c6.substring(2, 3));
        i3 = A4.indexOf(c6.substring(3, 4));
        i4 = A4.indexOf(c6.substring(4, 5));
        int n28 = i0;
        n28 = n28 * 36 + i1;
        n28 = n28 * 10 + i2;
        n28 = n28 * 27 + i3;
        n28 = n28 * 27 + i4;

        if (c6.length() >= 5) {
            i5 = A4.indexOf(c6.substring(5, 6));
            n28 = n28 * 27 + i5;
        }

        return NTOKENS + MAX22 + n28;
    }

    /**
     * 格式化标准的呼号
     * 标准呼号是6位，前缀是1~2个字母+1个数字，后缀对多3个字母
     * 格式化的内容：
     * 1.斯威士兰（Swaziland）的呼号前缀问题: 3DA0XYZ -> 3D0XYZ
     * 2.几内亚（Guinea）呼号前缀问题: 3XA0XYZ -> QA0XYZ
     * 3.第2位是数字的呼号，前面用空格补充。A0XYZ->" A0XYZ"
     * 4.后缀不足3位的，也要补足空格。BA2BI->"BA2BI "
     *
     * @param callsign 呼号
     * @return 返回C28值，以int的值来表示
     */
    private static String formatCallsign(String callsign) {
        String c6 = callsign;
        // 解决斯威士兰（Swaziland）的呼号前缀问题: 3DA0XYZ -> 3D0XYZ
        if (callsign.length() > 3 && callsign.substring(0, 4).equals("3DA0") && callsign.length() <= 7) {
            c6 = "3D0" + callsign.substring(4);
            // 解决几内亚（Guinea）呼号前缀问题: 3XA0XYZ -> QA0XYZ
        } else if (callsign.length() > 3 && callsign.substring(0, 3).matches("3X[A-Z]") && callsign.length() <= 7) {
            c6 = "Q" + callsign.substring(2);
        } else {
            // 第2位是数字,第3位是字母的，要在前面补充空格：A0XYZ -> " A0XYZ",A6开头的除外
            if (callsign.substring(0, 3).matches("[A-Z][0-9][A-Z]")) {
                c6 = " " + callsign;
            }
        }

        if (c6.length() < 6) {//如果长度不足6位，结尾补充空格
            for (int i = 0; i < 6 - c6.length() + 1; i++) {
                c6 = c6 + " ";
            }
        }

        return c6;
    }

    public static native int getHash12(String callsign);


    public static native int getHash10(String callsign);

    public static native int getHash22(String callsign);
}
