class OspfNbrImpl implements OspfNbr {

    public boolean processReceivedLsa(LsaHeader recLsa,
                                      boolean receivedViaFlooding, Channel ch, Ip4Address sourceIp)
            throws Exception {
        log.debug("OSPFNbr::processReceivedLsa(recLsa, recievedViaFlooding, ch)...!!!");

        //Validate the lsa checksum RFC 2328 13 (1)
        ChecksumCalculator checkSum = new ChecksumCalculator();
        if (!checkSum.isValidLsaCheckSum(recLsa,
                                         recLsa.getOspfLsaType().value(),
                                         OspfUtil.LSAPACKET_CHECKSUM_POS1,
                                         OspfUtil.LSAPACKET_CHECKSUM_POS2)) {
            log.debug("Checksum mismatch. Received LSA packet type {} ",
                      recLsa.lsType());

            return true;
        }

        //If LSA type is unknown discard the lsa RFC 2328 13(2)
        if (((recLsa.getOspfLsaType().value() > OspfLsaType.EXTERNAL_LSA.value()) &&
                (recLsa.getOspfLsaType().value() < OspfLsaType.LINK_LOCAL_OPAQUE_LSA.value())) ||
                (recLsa.getOspfLsaType().value() > OspfLsaType.AS_OPAQUE_LSA.value())) {
            return true;
        }

        //If LSA type is external & the area is configured as stub area discard the lsa RFC 2328 13(3)
        if ((recLsa.getOspfLsaType() == OspfLsaType.EXTERNAL_LSA) &&
                (!ospfArea.isExternalRoutingCapability())) {
            return true;
        }

        //if lsa age is equal to maxage && instance is not in lsdb && none of neighbors are in exchange
        // or loading state
        // Acknowledge the receipt by sending LSAck to the sender. 2328 13(4)
        if ((recLsa.age() == OspfParameters.MAXAGE) &&
                (ospfArea.lsaLookup(recLsa) == null) &&
                ospfArea.noNeighborInLsaExchangeProcess()) {
            // RFC 2328 Section 13. (4)
            // Because the LSA was not yet requested, it is treated as a flooded LSA and thus
            // acknowledged.
            directAcknowledge(recLsa, ch, sourceIp);
            return true;
        }

        String key = ((OspfAreaImpl) ospfArea).getLsaKey(recLsa);
        LsaWrapper lsWrapper = ospfArea.lsaLookup(recLsa);
        String status = isNullorLatest(lsWrapper, recLsa);
        //Section 13 (5)
        if (status.equals("isNullorLatest")) {

            if (recLsa.lsType() == OspfLsaType.ROUTER.value() && recLsa.advertisingRouter().equals(
                    ospfArea.routerId())) {
                if (recLsa.lsSequenceNo() > ((LsaWrapperImpl) lsWrapper).lsaHeader().lsSequenceNo()) {
                    ospfArea.setDbRouterSequenceNumber(recLsa.lsSequenceNo() + 1);
                    processSelfOriginatedLsa();
                }

                if (recLsa.age() == OspfParameters.MAXAGE) {
                    ((LsaWrapperImpl) lsWrapper).lsaHeader().setAge(OspfParameters.MAXAGE);
                    //remove from db & bin, add the lsa to MaxAge bin.
                    ospfArea.addLsaToMaxAgeBin(((OspfAreaImpl) ospfArea).getLsaKey(((LsaWrapperImpl)
                            lsWrapper).lsaHeader()), lsWrapper);
                    ospfArea.removeLsaFromBin(lsWrapper);
                }

                return true;
            } else if (recLsa.lsType() == OspfLsaType.NETWORK.value() && isLinkStateMatchesOwnRouterId(
                    recLsa.linkStateId())) {
                // if we are not DR or if origination router ID not equal to our router ID //either
                // DR state changed or our router ID was changed
                //set LSAge = MaxAge
                //flood the LSA
                if (((OspfInterfaceImpl) ospfInterface).state() != OspfInterfaceState.DR ||
                        !recLsa.advertisingRouter().equals(
                                ospfArea.routerId())) {
                    if (lsWrapper != null) {
                        ((LsaWrapperImpl) lsWrapper).lsaHeader().setAge(OspfParameters.MAXAGE);
                        //remove from bin, add the lsa to MaxAge bin.
                        ospfArea.addLsaToMaxAgeBin(((OspfAreaImpl) ospfArea).getLsaKey(((LsaWrapperImpl)
                                lsWrapper).lsaHeader()), lsWrapper);
                        ospfArea.removeLsaFromBin(lsWrapper);
                    } else {
                        recLsa.setAge(OspfParameters.MAXAGE);
                        ((OspfAreaImpl) ospfArea).addToOtherNeighborLsaTxList(recLsa);
                    }
                }

                return true;
            } else {
                if (recLsa.age() == OspfParameters.MAXAGE) {
                    ((OspfInterfaceImpl) ospfInterface).addLsaHeaderForDelayAck(recLsa);
                    //remove from db & bin, add the lsa to MaxAge bin.
                    if (lsWrapper != null) {
                        lsWrapper.setLsaAgeReceived(OspfParameters.MAXAGE);
                        ospfArea.addLsaToMaxAgeBin(((OspfAreaImpl) ospfArea).getLsaKey(((LsaWrapperImpl)
                                lsWrapper).lsaHeader()), lsWrapper);
                        ospfArea.removeLsaFromBin(lsWrapper);
                    } else {
                        ((OspfAreaImpl) ospfArea).addToOtherNeighborLsaTxList(recLsa);
                    }

                    return true;
                } else {
                    ospfArea.addLsa(recLsa, ospfInterface);
                    log.debug("Inside addLsaMethod");
                    topologyForDeviceAndLink.addLocalDevice(recLsa, ospfInterface, ospfArea);
                    callDeviceAndLinkAdding(topologyForDeviceAndLink);
                    log.debug("Adding to lsdb interface State {}", ((OspfInterfaceImpl) ospfInterface).state().value());
                    // should not send any acknowledge if flooded out on receiving interface
                    if (((OspfInterfaceImpl) ospfInterface).state().value() == OspfInterfaceState.BDR.value()) {
                        if (neighborDr.equals(sourceIp)) {
                            log.debug("Adding for delayed ack {}", recLsa);
                            ((OspfInterfaceImpl) ospfInterface).addLsaHeaderForDelayAck(recLsa);
                        }
                    } else {
                        log.debug("Adding for delayed ack {}", recLsa);
                        ((OspfInterfaceImpl) ospfInterface).addLsaHeaderForDelayAck(recLsa);
                    }

                    if (((OspfInterfaceImpl) ospfInterface).state().value() == OspfInterfaceState.DR.value() ||
                            ((OspfInterfaceImpl) ospfInterface).state().value() ==
                                    OspfInterfaceState.POINT2POINT.value()) {
                        ((OspfAreaImpl) ospfArea).addToOtherNeighborLsaTxList(recLsa);
                    }
                }

            }
        }
        // RFC 2328 Section 13  (6)
        if (lsReqList.contains(key)) {
            badLSReq(ch);
        }
        if (status.equals("same")) { //13 (7)
            if (pendingReTxList.containsKey(key)) {
                pendingReTxList.remove(key);
                if (((OspfInterfaceImpl) ospfInterface).state().value() == OspfInterfaceState.BDR.value()) {
                    if (neighborDr.equals(recLsa.advertisingRouter())) {
                        ((OspfInterfaceImpl) ospfInterface).addLsaHeaderForDelayAck(recLsa);
                    }
                }
            } else {
                directAcknowledge(recLsa, ch, sourceIp);
                return true;
            }
        } else if (status.equals("old")) { // section 13 - point 8
            if ((recLsa.lsSequenceNo() == OspfParameters.MAXSEQUENCENUMBER) &&
                    (recLsa.age() == OspfParameters.MAXAGE)) {
                // section 13 - point 8
                // simple discard the received LSA -
                return true;
            } else {
                // respond back with the same LSA
                //Using flood LSA to sent the LSUpdate back to advertising router
                int diff = Math.abs(lsWrapper.lsaAgeReceived() - recLsa.age());
                if (diff > OspfParameters.MINLSARRIVAL) {
                    sendLsa(((LsaWrapperImpl) lsWrapper).lsaHeader(), sourceIp, ch);
                }
            }
        }

        constructDeviceInformationFromDb();
        callDeviceAndLinkAdding(topologyForDeviceAndLink);

        return true;
    }

    public  OspfNbrImpl(OspfArea paramOspfArea, OspfInterface paramOspfInterface,
                       Ip4Address ipAddr, Ip4Address routerId, int options,
                       TopologyForDeviceAndLink topologyForDeviceAndLinkCommon);

}