package net.freertr.pack;

import java.util.Comparator;
import net.freertr.addr.addrIP;
import net.freertr.addr.addrIPv4;
import net.freertr.addr.addrIPv6;
import net.freertr.cfg.cfgAll;
import net.freertr.cfg.cfgIfc;
import net.freertr.util.bits;
import net.freertr.util.cmds;

/**
 * domain name protocol (rfc1035) resource record
 *
 * @author matecsaba
 */
public class packDnsRes implements Comparator<packDnsRes> {

    /**
     * create instance
     */
    public packDnsRes() {
    }

    /**
     * target address
     */
    public String target = "";

    /**
     * email address
     */
    public String email = "";

    /**
     * sequence
     */
    public int sequence;

    /**
     * refresh
     */
    public int fresh;

    /**
     * retry
     */
    public int retry;

    /**
     * expire
     */
    public int expire;

    /**
     * min ttl
     */
    public int minttl;

    /**
     * address
     */
    public addrIP addr = new addrIP();

    /**
     * interface
     */
    public cfgIfc iface;

    public String toString() {
        return "target=" + target + " os=" + email + " addr=" + addr + " seq=" + sequence + " fresh=" + fresh + " retry=" + retry + " expire=" + expire + " minttl=" + minttl;
    }

    /**
     * copy bytes
     *
     * @return dns record
     */
    public packDnsRes copyBytes() {
        packDnsRes n = new packDnsRes();
        n.target = target;
        n.email = email;
        n.sequence = sequence;
        n.fresh = fresh;
        n.retry = retry;
        n.expire = expire;
        n.minttl = minttl;
        n.addr = addr.copyBytes();
        n.iface = iface;
        return n;
    }

    /**
     * convert to user string
     *
     * @param typ type of record
     * @return user string
     */
    public String toUserStr(int typ) {
        switch (typ) {
            case packDnsRec.typeCNAME:
                return target;
            case packDnsRec.typeHINFO:
                return target + " " + email;
            case packDnsRec.typeMB:
            case packDnsRec.typeMD:
            case packDnsRec.typeMF:
            case packDnsRec.typeMG:
            case packDnsRec.typeMR:
                return target;
            case packDnsRec.typeMINFO:
            case packDnsRec.typeRP:
                return target + " " + email;
            case packDnsRec.typeMX:
                return sequence + " " + target;
            case packDnsRec.typeSOA:
                return target + " " + email + " " + sequence + " " + fresh + " " + retry + " " + expire + " " + minttl;
            case packDnsRec.typeNULL:
            case packDnsRec.typeNS:
            case packDnsRec.typePTR:
            case packDnsRec.typeTXT:
                return target;
            case packDnsRec.typeSRV:
                return sequence + " " + fresh + " " + retry + " " + target;
            case packDnsRec.typeAAAA:
            case packDnsRec.typeA6:
            case packDnsRec.typeA:
                return "" + addr;
            case packDnsRec.typeInt4:
            case packDnsRec.typeInt6:
                return iface.name;
            default:
                return null;
        }

    }

    /**
     * convert from user string
     *
     * @param typ type of record
     * @param cmd command to parse
     * @return false if successful, true if error happened
     */
    public boolean fromUserStr(int typ, cmds cmd) {
        switch (typ) {
            case packDnsRec.typeA:
                addrIPv4 adr4 = new addrIPv4();
                if (adr4.fromString(cmd.word())) {
                    return true;
                }
                addr.fromIPv4addr(adr4);
                return false;
            case packDnsRec.typeAAAA:
                addrIPv6 adr6 = new addrIPv6();
                if (adr6.fromString(cmd.word())) {
                    return true;
                }
                addr.fromIPv6addr(adr6);
                return false;
            case packDnsRec.typeInt4:
                iface = cfgAll.ifcFind(cmd.word(), false);
                if (iface == null) {
                    cmd.error("no such interface");
                    return true;
                }
                return false;
            case packDnsRec.typeInt6:
                iface = cfgAll.ifcFind(cmd.word(), false);
                if (iface == null) {
                    cmd.error("no such interface");
                    return true;
                }
                return false;
            case packDnsRec.typeCNAME:
                target = cmd.word();
                return false;
            case packDnsRec.typePTR:
                target = cmd.word();
                return false;
            case packDnsRec.typeNS:
                target = cmd.word();
                return false;
            case packDnsRec.typeRP:
                target = cmd.word();
                email = cmd.word();
                return false;
            case packDnsRec.typeMX:
                sequence = bits.str2num(cmd.word());
                target = cmd.word();
                return false;
            case packDnsRec.typeTXT:
                target = cmd.getRemaining();
                return false;
            case packDnsRec.typeSRV:
                sequence = bits.str2num(cmd.word());
                fresh = bits.str2num(cmd.word());
                retry = bits.str2num(cmd.word());
                target = cmd.getRemaining();
                return false;
            case packDnsRec.typeHINFO:
                target = cmd.word();
                email = cmd.word();
                return false;
            case packDnsRec.typeSOA:
                target = cmd.word();
                email = cmd.word();
                sequence = bits.str2num(cmd.word());
                fresh = bits.str2num(cmd.word());
                retry = bits.str2num(cmd.word());
                expire = bits.str2num(cmd.word());
                minttl = bits.str2num(cmd.word());
                return false;
            default:
                return true;
        }
    }

    /**
     * parse header
     *
     * @param typ type of record
     * @param pck packet to use
     * @param len message ending
     * @return false if successful, true if error happened
     */
    public boolean parseHeader(int typ, packHolder pck, int len) {
        switch (typ) {
            case packDnsRec.typeCNAME:
                target = packDnsRec.getChain(pck, len, 0);
                return false;
            case packDnsRec.typeHINFO:
                target = packDnsRec.getString(pck);
                email = packDnsRec.getString(pck);
                return false;
            case packDnsRec.typeMB:
            case packDnsRec.typeMD:
            case packDnsRec.typeMF:
            case packDnsRec.typeMG:
            case packDnsRec.typeMR:
                target = packDnsRec.getChain(pck, len, 0);
                return false;
            case packDnsRec.typeMINFO:
            case packDnsRec.typeRP:
                target = packDnsRec.getChain(pck, len, 0);
                email = packDnsRec.getChain(pck, len, 0);
                return false;
            case packDnsRec.typeMX:
                sequence = pck.msbGetW(0);
                pck.getSkip(2);
                target = packDnsRec.getChain(pck, len, 0);
                return false;
            case packDnsRec.typeNS:
            case packDnsRec.typePTR:
                target = packDnsRec.getChain(pck, len, 0);
                return false;
            case packDnsRec.typeSOA:
                target = packDnsRec.getChain(pck, len, 0);
                email = packDnsRec.getChain(pck, len, 0);
                sequence = pck.msbGetD(0);
                fresh = pck.msbGetD(4);
                retry = pck.msbGetD(8);
                expire = pck.msbGetD(12);
                minttl = pck.msbGetD(16);
                pck.getSkip(20);
                return false;
            case packDnsRec.typeNULL:
            case packDnsRec.typeTXT:
                target = packDnsRec.getString(pck);
                return false;
            case packDnsRec.typeSRV:
                sequence = pck.msbGetW(0);
                fresh = pck.msbGetW(2);
                retry = pck.msbGetW(4);
                pck.getSkip(6);
                target = packDnsRec.getChain(pck, len, 0);
                return false;
            case packDnsRec.typeA:
                addrIPv4 adr4 = new addrIPv4();
                pck.getAddr(adr4, 0);
                addr.fromIPv4addr(adr4);
                return false;
            case packDnsRec.typeAAAA:
            case packDnsRec.typeA6:
                addrIPv6 adr6 = new addrIPv6();
                pck.getAddr(adr6, 0);
                addr.fromIPv6addr(adr6);
                return false;
            default:
                return true;
        }
    }

    /**
     * create header
     *
     * @param typ type of record
     * @param pck packet to use
     */
    public void createHeader(int typ, packHolder pck) {
        int ofs = pck.headSize();
        switch (typ) {
            case packDnsRec.typeCNAME:
                packDnsRec.putDomain(pck, target);
                break;
            case packDnsRec.typeHINFO:
                packDnsRec.putString(pck, target);
                packDnsRec.putString(pck, email);
                break;
            case packDnsRec.typeMB:
            case packDnsRec.typeMD:
            case packDnsRec.typeMF:
            case packDnsRec.typeMG:
            case packDnsRec.typeMR:
                packDnsRec.putDomain(pck, target);
                break;
            case packDnsRec.typeMINFO:
            case packDnsRec.typeRP:
                packDnsRec.putDomain(pck, target);
                packDnsRec.putDomain(pck, email);
                break;
            case packDnsRec.typeMX:
                pck.msbPutW(0, sequence);
                pck.putSkip(2);
                packDnsRec.putDomain(pck, target);
                break;
            case packDnsRec.typeNULL:
            case packDnsRec.typeTXT:
                packDnsRec.putString(pck, target);
                break;
            case packDnsRec.typeSRV:
                pck.msbPutW(0, sequence);
                pck.msbPutW(2, fresh);
                pck.msbPutW(4, retry);
                pck.putSkip(6);
                packDnsRec.putDomain(pck, target);
                break;
            case packDnsRec.typeNS:
            case packDnsRec.typePTR:
                packDnsRec.putDomain(pck, target);
                break;
            case packDnsRec.typeSOA:
                packDnsRec.putDomain(pck, target);
                packDnsRec.putDomain(pck, email);
                pck.msbPutD(0, sequence);
                pck.msbPutD(4, fresh);
                pck.msbPutD(8, retry);
                pck.msbPutD(12, expire);
                pck.msbPutD(16, minttl);
                pck.putSkip(20);
                break;
            case packDnsRec.typeA:
                addrIPv4 adr4 = addr.toIPv4();
                pck.putAddr(0, adr4);
                pck.putSkip(addrIPv4.size);
                break;
            case packDnsRec.typeAAAA:
            case packDnsRec.typeA6:
                addrIPv6 adr6 = addr.toIPv6();
                pck.putAddr(0, adr6);
                pck.putSkip(addrIPv6.size);
                break;
            case packDnsRec.typeInt4:
                adr4 = iface.addr4;
                pck.putAddr(0, adr4);
                pck.putSkip(addrIPv4.size);
                break;
            case packDnsRec.typeInt6:
                adr6 = iface.addr6;
                pck.putAddr(0, adr6);
                pck.putSkip(addrIPv6.size);
                break;
            default:
                break;
        }
        int siz = pck.headSize() - ofs;
        pck.msbPutW(-siz - 2, siz);
    }

    public int compare(packDnsRes o1, packDnsRes o2) {

        if (o1.sequence < o2.sequence) {
            return -1;
        }
        if (o1.sequence > o2.sequence) {
            return +1;
        }
        if (o1.fresh < o2.fresh) {
            return -1;
        }
        if (o1.fresh > o2.fresh) {
            return +1;
        }
        if (o1.retry < o2.retry) {
            return -1;
        }
        if (o1.retry > o2.retry) {
            return +1;
        }
        if (o1.expire < o2.expire) {
            return -1;
        }
        if (o1.expire > o2.expire) {
            return +1;
        }
        if (o1.minttl < o2.minttl) {
            return -1;
        }
        if (o1.minttl > o2.minttl) {
            return +1;
        }
        int i = addr.compare(o1.addr, o2.addr);
        if (i != 0) {
            return i;
        }
        i = o1.target.compareTo(o2.target);
        if (i != 0) {
            return i;
        }
        i = o1.email.compareTo(o2.email);
        if (i != 0) {
            return i;
        }
        if (o1.iface == null) {
            if (o2.iface == null) {
                return 0;
            }
            return +1;
        } else {
            if (o2.iface == null) {
                return -1;
            }
            return o1.iface.compare(o1.iface, o2.iface);
        }
    }

}
