/* Copyright (c) 2018 -- 2022
   Charles Jordan (Hokkaido University, Sapporo, Japan),
   Michael Joswig, Lars Kastner (Technische Universitaet Berlin, Germany)

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 3, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   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 General Public License for more details.
-----------------------------------------------------------------------------
*/

#ifndef FLIPDATASIMPLE_HH
#define FLIPDATASIMPLE_HH
#include "FlipData_core.hh"




template<typename FlipChecker, typename APCType>
class FlipDataSimple : public FlipData_core<FlipChecker, typename APCType::pmFlipContainer>{
   
   using pmFlipContainer = typename APCType::pmFlipContainer;
   typedef FlipData_core<FlipChecker, pmFlipContainer> FDC;
   friend class FlipData_core<FlipChecker, pmFlipContainer>;

   private:
   
      using OwnType = FlipDataSimple<FlipChecker, APCType>;
      friend void core_add_new_flips<OwnType>(OwnType&, const SimplicialComplex&, const bool);
      const APCType& apc;

      // methods:
      void _remove_destroyed_flips(const Flip& flip);

      void insert_raw_flip(Flip flip);
      

      Int is_lex_upflip(Flip flip, gkz_vector_type& result_gkz){
         return this->apc.is_lex_upflip(flip, result_gkz);
      }


      void insert_flip(Flip flip, gkz_vector_type flip_gkz, Int isUpflip){
         if(isUpflip == 1){
            this->upflips.insert(flip_gkz, flip);
         } else {
            this->downflips.insert(flip_gkz, flip);
         }
      }
      
      
      void mutate_inner(const Flip& flip){
         _remove_destroyed_flips(flip);
         core_add_new_flips(*this, flip.second, false);
      }

      // Mutation constructor
      FlipDataSimple(
            const APCType& apc_in,
            TriangNode triang,
            const pmFlipContainer& uf,
            const pmFlipContainer& df,
            const FlipChecker& fc_in
            ): FDC(triang, uf, df, fc_in, apc_in.get_chiro()), apc(apc_in)
         {}


   public:
      typedef FlipDataSimple<typename FlipChecker::Initial, APCType> Initial;
      typedef FlipChecker Checker;
      
      FlipDataSimple(
            const APCType& apc_in,
            TriangNode triang,
            const FlipChecker& fc_in
            ): FDC(triang, fc_in, apc_in.get_chiro()), apc(apc_in)
         {
            core_add_new_flips(*this, triang, false);
         }
      

      void mutate(const Flip& flip){
         this->source_triang = TriangNode(0, this->source_triang, flip);
         mutate_inner(flip);
      }

      FlipDataSimple mutate(const Flip& flip, const TriangNode& target){
         FlipDataSimple result(this->apc, target, this->upflips, this->downflips, this->fc);
         result.mutate_inner(flip);
         return result;
      }


};

template<typename FlipChecker, typename APCType>
void FlipDataSimple<FlipChecker, APCType>::insert_raw_flip(Flip flip){
   gkz_vector_type current_gkz;
   Int isUpflip = is_lex_upflip(flip, current_gkz);
   insert_flip(flip, current_gkz, isUpflip);
}



template<typename FlipChecker, typename APCType>
void FlipDataSimple<FlipChecker, APCType>::_remove_destroyed_flips(const Flip& flip) {
   // FIXME
   // Not sure how this should be done. The problem is that we want to loop
   // over the flips, while erasing some (hopefully in an intelligent way).
   // Attempts:
   // 1. Copy the maps and loop over the copies.
   // 2. Remember the things we need to remove and remove them later. Better
   //    than 1.
   std::vector<typename pmFlipContainer::iterator> toRemove;
   for(auto upf = this->upflips.begin();
         upf != this->upflips.end();
         ++upf){
      if(!((*upf).second.first * flip.first).is_empty()){
         toRemove.push_back(upf);
      }
   }
   while(toRemove.size() > 0){
      this->upflips.erase(toRemove.back());
      toRemove.pop_back();
   }
   for(auto downf = this->downflips.begin();
         downf != this->downflips.end();
         ++downf){
      if(!((*downf).second.first * flip.first).is_empty()){
         toRemove.push_back(downf);
      }
   }
   while(toRemove.size() > 0){
      this->downflips.erase(toRemove.back());
      //      downflips.erase(toRemove.back());
      toRemove.pop_back();
   }
}

#endif
