/**
  * Decode a full taf string into a complete taf object
  * @param $raw_taf
  * @param $strict
  * @return DecodedTaf
  */
 private function parseWithMode($raw_taf, $strict)
 {
     // prepare decoding inputs/outputs: (trim, remove linefeeds and returns, no more than one space)
     $clean_taf = trim($raw_taf);
     $clean_taf = preg_replace("#\n+#", ' ', $clean_taf);
     $clean_taf = preg_replace("#\r+#", ' ', $clean_taf);
     $clean_taf = preg_replace('#[ ]{2,}#', ' ', $clean_taf) . ' ';
     $clean_taf = strtoupper($clean_taf);
     if (strpos($clean_taf, 'CNL') === false) {
         // appending END to it is necessary to detect the last line of evolution
         // but only when the TAF wasn't cancelled (CNL)
         $remaining_taf = trim($clean_taf) . ' END';
     } else {
         $remaining_taf = $clean_taf;
     }
     $decoded_taf = new DecodedTaf($clean_taf);
     $with_cavok = false;
     // call each decoder in the chain and use results to populate decoded taf
     foreach ($this->decoder_chain as $chunk_decoder) {
         try {
             // try to parse a chunk with current chunk decoder
             $decoded = $chunk_decoder->parse($remaining_taf, $with_cavok);
             // map obtained fields (if any) to the final decoded object
             $result = $decoded['result'];
             if ($result != null) {
                 foreach ($result as $key => $value) {
                     if ($value !== null) {
                         $setter_name = 'set' . ucfirst($key);
                         $decoded_taf->{$setter_name}($value);
                     }
                 }
             }
             // update remaining taf for next round
             $remaining_taf = $decoded['remaining_taf'];
         } catch (ChunkDecoderException $cde) {
             // log error in decoded taf and abort decoding if in strict mode
             $decoded_taf->addDecodingException($cde);
             // abort decoding if strict mode is activated, continue otherwise
             if ($strict) {
                 break;
             }
             // update remaining taf for next round
             $remaining_taf = $cde->getRemainingTaf();
         }
         // hook for CAVOK decoder, keep CAVOK information in memory
         if ($chunk_decoder instanceof VisibilityChunkDecoder) {
             $with_cavok = $decoded_taf->getCavok();
         }
     }
     // weather evolutions
     $evolutionDecoder = new EvolutionChunkDecoder($strict, $with_cavok);
     while ($remaining_taf != null && trim($remaining_taf) != 'END') {
         $evolutionDecoder->parse($remaining_taf, $decoded_taf);
         $remaining_taf = $evolutionDecoder->getRemaining();
     }
     return $decoded_taf;
 }
 /**
  * Add the evolution to the decodedTaf's entity
  *
  * @param DecodedTaf    $decoded_taf
  * @param Evolution     $evolution
  * @param array         $result
  * @param string        $entity_name
  */
 private function addEvolution($decoded_taf, $evolution, $result, $entity_name)
 {
     // clone the evolution entity
     /** @var Evolution $newEvolution */
     $new_evolution = clone $evolution;
     // add the new entity to it
     $new_evolution->setEntity($result[$entity_name]);
     // possibly add cavok to it
     if ($entity_name == 'visibility' && $this->with_cavok == true) {
         $new_evolution->setCavok(true);
     }
     // get the original entity from the decoded taf or a new one decoded taf doesn't contain it yet
     $getter_name = 'get' . ucfirst($entity_name);
     $setter_name = 'set' . ucfirst($entity_name);
     $decoded_entity = $decoded_taf->{$getter_name}();
     if ($decoded_entity == null || $entity_name == 'clouds') {
         // that entity is not in the decoded_taf yet, or it's a cloud layer which is a special case
         $decoded_entity = $this->instantiateEntity($entity_name);
     }
     // add the new evolution to that entity
     $decoded_entity->addEvolution($new_evolution);
     // update the decoded taf's entity or add the new one to it
     if ($entity_name == 'clouds') {
         $decoded_taf->addCloud($decoded_entity);
     } else {
         $decoded_taf->{$setter_name}($decoded_entity);
     }
 }