text: //
  // Copyright (C) 2016 OpenSim Ltd.
  //
  // This program is free software; you can redistribute it and/or
  // modify it under the terms of the GNU Lesser General Public License
  // as published by the Free Software Foundation; either version 2
  // of the License, or (at your option) any later version.
  //
  // This program is distributed in the hope that it will be useful,
  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  // GNU Lesser General Public License for more details.
  //
  // You should have received a copy of the GNU Lesser General Public License
  // along with this program; if not, see http://www.gnu.org/licenses/.
  //
  
  #include "inet/linklayer/ieee80211/mac/framesequence/DcfFs.h"
  #include "inet/linklayer/ieee80211/mac/framesequence/PrimitiveFrameSequences.h"
  
  namespace inet {
  namespace ieee80211 {
  
  DcfFs::DcfFs() :
      // Excerpt from G.2 Basic sequences (p. 2309)
      // frame-sequence =
      //   ( [ CTS ] ( Management + broadcast | Data + group ) ) |
      //   ( [ CTS | RTS CTS] {frag-frame ACK } last-frame ACK )
      //
      // frag-frame = ( Data | Management ) + individual + frag;
      // last-frame = ( Data | Management ) + individual + last;
      //
      AlternativesFs({new SequentialFs({new OptionalFs(new SelfCtsFs(), OPTIONALFS_PREDICATE(isSelfCtsNeeded)),
                                        new AlternativesFs({new ManagementFs(), new DataFs()}, ALTERNATIVESFS_SELECTOR(selectMulticastDataOrMgmt))}),
                      new SequentialFs({new OptionalFs(new AlternativesFs({new SelfCtsFs(), new SequentialFs({new RtsFs(), new CtsFs()})},
                                                                          ALTERNATIVESFS_SELECTOR(selectSelfCtsOrRtsCts)),
                                                       OPTIONALFS_PREDICATE(isCtsOrRtsCtsNeeded)),
                                        new RepeatingFs(new FragFrameAckFs(), REPEATINGFS_PREDICATE(hasMoreFragments)),
                                        new LastFrameAckFs()})},
                     ALTERNATIVESFS_SELECTOR(selectDcfSequence))
  {
  }
  
  int DcfFs::selectDcfSequence(AlternativesFs *frameSequence, FrameSequenceContext *context)
  {
      bool multicastMgmtOrDataSequence = isBroadcastManagementOrGroupDataSequenceNeeded(frameSequence, context);
      bool fragFrameSequence = isFragFrameSequenceNeeded(frameSequence, context);
      if (multicastMgmtOrDataSequence) return 0;
      else if (fragFrameSequence) return 1;
      else throw cRuntimeError("One alternative must be chosen");
  }
  
  int DcfFs::selectSelfCtsOrRtsCts(AlternativesFs *frameSequence, FrameSequenceContext *context)
  {
      bool selfCts = isSelfCtsNeeded(nullptr, context); // TODO
      bool rtsCts = isRtsCtsNeeded(nullptr, context); // TODO
      if (selfCts) return 0;
      else if (rtsCts) return 1;
      else throw cRuntimeError("One alternative must be chosen");
  }
  
  bool DcfFs::hasMoreFragments(RepeatingFs *frameSequence, FrameSequenceContext *context)
  {
      return context->getInProgressFrames()->hasInProgressFrames() && context->getInProgressFrames()->getFrameToTransmit()->getMoreFragments();
  }
  
  bool DcfFs::isSelfCtsNeeded(OptionalFs *frameSequence, FrameSequenceContext *context)
  {
      return false; // TODO: Implement
  }
  
  bool DcfFs::isRtsCtsNeeded(OptionalFs *frameSequence, FrameSequenceContext *context)
  {
      auto protectedFrame = context->getInProgressFrames()->getFrameToTransmit();
      return context->getRtsPolicy()->isRtsNeeded(protectedFrame);
  }
  
  bool DcfFs::isCtsOrRtsCtsNeeded(OptionalFs *frameSequence, FrameSequenceContext *context)
  {
      bool selfCts = isSelfCtsNeeded(frameSequence, context);
      bool rtsCts = isRtsCtsNeeded(frameSequence, context);
      return selfCts || rtsCts;
  }
  
  bool DcfFs::isBroadcastManagementOrGroupDataSequenceNeeded(AlternativesFs *frameSequence, FrameSequenceContext *context)
  {
      if (context->getInProgressFrames()->hasInProgressFrames()) {
          auto frameToTransmit = context->getInProgressFrames()->getFrameToTransmit();
          return frameToTransmit->getReceiverAddress().isMulticast();
      }
      else
          return false;
  }
  
  int DcfFs::selectMulticastDataOrMgmt(AlternativesFs* frameSequence, FrameSequenceContext* context)
  {
      auto frameToTransmit = context->getInProgressFrames()->getFrameToTransmit();
      return dynamic_cast<Ieee80211ManagementFrame*>(frameToTransmit) ? 0 : 1;
  }
  
  bool DcfFs::isFragFrameSequenceNeeded(AlternativesFs *frameSequence, FrameSequenceContext *context)
  {
      return context->getInProgressFrames()->hasInProgressFrames() && dynamic_cast<Ieee80211DataOrMgmtFrame*>(context->getInProgressFrames()->getFrameToTransmit());
  }
  
  } // namespace ieee80211
  } // namespace inet
  