/*
Copyright (c) 1996,1997,1998,1999,2000,2001,2004,2006,2007,2008
Whitehead Institute for Biomedical Research, Steve Rozen
(http://purl.com/STEVEROZEN/), Andreas Untergasser and Helen Skaletsky
All rights reserved.

    This file is part of primer3 and the primer3 suite.

    Primer3 and the primer3 suite are free software;
    you can redistribute them and/or modify them under the terms
    of the GNU General Public License as published by the Free
    Software Foundation; either version 2 of the License, or (at
    your option) any later version.

    This software 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.

    You should have received a copy of the GNU General Public License
    along with this file (file gpl-2.0.txt in the source
    distribution); if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/
#define _CRT_SECURE_NO_DEPRECATE

#include <string.h> /* strcpy */
#include "print_boulder.h"

static const char *pr_program_name = "Program name is probably primer3_core";

static void   print_all_explain(const p3_global_settings *,
                                const seq_args *, 
                                const p3retval *, 
                                int   io_version);

/* Print the data for chosen primer pairs to stdout in "boulderio" format. */
void
print_boulder(int io_version,
              const p3_global_settings *pa,
              const seq_args *sa,
              const p3retval *retval,
              int   explain_flag) {
  /* The pointers to warning tag */
  char *warning;

  /* A place to put a string containing all error messages */
  pr_append_str *combined_retval_err = NULL;

  /* A small spacer; WARNING this is a fixed size
     buffer, but plenty bigger than
     log(2^64, 10), the longest character
     string that is needed for a 64 bit integer. */
  char suffix [100];

  /* Pointers for the primer set just printing */
  primer_rec *fwd, *rev, *intl;
 
  /* Variables only used for Primer Lists */
  int num_fwd, num_rev, num_int, num_pair, num_print;
  int print_fwd = 0;
  int print_rev = 0;
  int print_int = 0;
    
  /* Switches for printing this primer */
  int go_fwd = 0;
  int go_rev = 0;
  int go_int = 0;
    
  /* The number of loop cycles */
  int loop_max;
    
  /* That links to the included region */
  int i, incl_s = sa->incl_s;
    
  /* This deals with the renaming of the internal oligo */
  const char *new_oligo_name = "INTERNAL";
  char *int_oligo = (char*) new_oligo_name;

  /* Check: are all pointers linked to something*/
  PR_ASSERT(NULL != pa);
  PR_ASSERT(NULL != sa);
        
  /* Check if there are warnings and print them */
  if ((warning = p3_get_rv_and_gs_warnings(retval, pa)) != NULL) { 
    printf("PRIMER_WARNING=%s\n", warning);
    free(warning);
  }

  /* Check if a settings file was read an print its id 
   * Not needed anymore, we do this when we read the setting file. */
  /*
  if (pa->settings_file_id != NULL) { 
    printf("P3_FILE_ID=%s\n", pa->settings_file_id);
    free(warning);
  }
  */

  combined_retval_err = create_pr_append_str();
  if (NULL == combined_retval_err) exit(-2); /* Out of memory */

  if (pr_append_new_chunk_external(combined_retval_err, 
                                   retval->glob_err.data))
    exit(-2);

  if (pr_append_new_chunk_external(combined_retval_err, 
                                   retval->per_sequence_err.data)) 
    exit(-2);

  /* Check if there are errors, print and return */
  if (!pr_is_empty(combined_retval_err)) {
    print_boulder_error(pr_append_str_chars(combined_retval_err));
    destroy_pr_append_str(combined_retval_err);
    return;
  }
  destroy_pr_append_str(combined_retval_err);

  /* Get how many primers are in the array */
  num_fwd = retval->fwd.num_elem;
  num_rev = retval->rev.num_elem;
  num_int = retval->intl.num_elem;
  num_pair = retval->best_pairs.num_pairs;

  /* Prints out statistics about the primers */
  if (explain_flag) print_all_explain(pa, sa, retval, io_version);
    
  /* Print out the stop codon if a reading frame was specified */
  if (!PR_START_CODON_POS_IS_NULL(sa))
    printf("PRIMER_STOP_CODON_POSITION=%d\n", retval->stop_codon_pos);
    
  /* How often has the loop to be done? */
  if (retval->output_type == primer_list) {
    /* For Primer Lists: Figure out how many primers are in
     * the array that can be printed. If more than needed,
     * set it to the number requested. */

    /* Get how may primers should be printed */
    num_print = pa->num_return;
    /* Set how many primers will be printed */
    print_fwd = (num_print < num_fwd) ? num_print : num_fwd;
    print_rev = (num_print < num_rev) ? num_print : num_rev;
    print_int = (num_print < num_int) ? num_print : num_int;
    /* Get which list has to print most primers */
    loop_max = 0;
    if (loop_max < print_fwd) {
      loop_max = print_fwd;
    }
    if (loop_max < print_rev) {
      loop_max = print_rev;
    }
    if (loop_max < print_int) {
      loop_max = print_int;
    }
    /* Now the vars are there how often we have to go
     * through the loop and how many of each primer can
     * be printed. */
    num_pair = 0;
  } else {
    loop_max = num_pair;
    /* Set how many primers will be printed */
    print_fwd = num_pair;
    print_rev = num_pair;
    if (num_int != 0) {
      print_int = num_pair;
    }
  }

  if (io_version == 4) {
    printf("PRIMER_LEFT_NUM_RETURNED=%d\n", print_fwd);
    printf("PRIMER_RIGHT_NUM_RETURNED=%d\n",  print_rev);
    printf("PRIMER_%s_NUM_RETURNED=%d\n", int_oligo, print_int);
    printf("PRIMER_PAIR_NUM_RETURNED=%d\n", num_pair);
  }
  
  /* --------------------------------------- */
  /* Start of the loop printing all pairs or primers or oligos */
  for(i=0; i<loop_max; i++) {
    /* What needs to be printed */
    /* The conditions for primer lists */

    if (retval->output_type == primer_list) {
      /* Attach the selected primers to the pointers */
      fwd = &retval->fwd.oligo[i];
      rev = &retval->rev.oligo[i];
      intl = &retval->intl.oligo[i];
      /* Do fwd oligos have to be printed? */
      if ((pa->pick_left_primer) && (i < print_fwd)) {
        go_fwd = 1;
      } else {
        go_fwd = 0;
      }
      /* Do rev oligos have to be printed? */
      if ((pa->pick_right_primer) && (i < print_rev)) {
        go_rev = 1;
      } else {
        go_rev = 0;
      }
      /* Do int oligos have to be printed? */
      if ((pa->pick_internal_oligo) && (i < print_int)) {
        go_int = 1;
      } else {
        go_int = 0;
      }
    }  else {
      /* We will print primer pairs or pairs plus internal oligos */
      /* Get pointers to the primer_rec's that we will print */
      fwd  = retval->best_pairs.pairs[i].left;
      rev  = retval->best_pairs.pairs[i].right;
      intl = retval->best_pairs.pairs[i].intl;
      /* Pairs must have fwd and rev primers */
      go_fwd = 1;
      go_rev = 1;
      /* Do hyb oligos have to be printed? */
      if (pa->pick_internal_oligo == 1) {
        go_int = 1;
      } else {
        go_int = 0;
      }
    }
      
    /* Get the number for pimer counting in suffix[0] */
    sprintf(suffix, "_%d", i);
    
    /* Print out the Pair Penalties */
    if (retval->output_type == primer_pairs) {
      printf("PRIMER_PAIR%s_PENALTY=%f\n", suffix,
             retval->best_pairs.pairs[i].pair_quality);
    }

    /* Print single primer penalty */
    if (go_fwd == 1)
      printf("PRIMER_LEFT%s_PENALTY=%f\n", suffix, fwd->quality);
    if (go_rev == 1)
      printf("PRIMER_RIGHT%s_PENALTY=%f\n", suffix, rev->quality);
    if (go_int == 1)
      printf("PRIMER_%s%s_PENALTY=%f\n", int_oligo, suffix, intl->quality);

    /* Print the oligo_problems */
    if (io_version == 4) {
      if (go_fwd == 1 && p3_ol_has_any_problem(fwd))
        printf("PRIMER_LEFT%s_PROBLEMS=%s\n", suffix, p3_get_ol_problem_string(fwd));
      if (go_rev == 1 && p3_ol_has_any_problem(rev))
        printf("PRIMER_RIGHT%s_PROBLEMS=%s\n", suffix, p3_get_ol_problem_string(rev));
      if (go_int == 1 && p3_ol_has_any_problem(intl))
        printf("PRIMER_%s%s_PROBLEMS=%s\n", int_oligo, suffix, p3_get_ol_problem_string(intl));
    }

    /* Print primer sequences. */
    if (go_fwd == 1)
      printf("PRIMER_LEFT%s_SEQUENCE=%s\n", suffix,
             pr_oligo_overhang_sequence(sa, fwd));
    if (go_rev == 1)
      printf("PRIMER_RIGHT%s_SEQUENCE=%s\n", suffix,
             pr_oligo_rev_c_overhang_sequence(sa, rev));
    if(go_int == 1)
      printf("PRIMER_%s%s_SEQUENCE=%s\n", int_oligo, suffix,
             pr_oligo_sequence(sa,intl));
        
    /* Print primer start and length */
    if (go_fwd == 1)
      printf("PRIMER_LEFT%s=%d,%d\n", suffix,
             fwd->start + incl_s + pa->first_base_index,
             fwd->length);
    if (go_rev == 1)
      printf("PRIMER_RIGHT%s=%d,%d\n", suffix,
             rev->start + incl_s + pa->first_base_index,
             rev->length);
    if (go_int == 1)
      printf("PRIMER_%s%s=%d,%d\n", int_oligo, suffix,
             intl->start + incl_s + pa->first_base_index,
             intl->length);

    /* Print primer Tm */
    if (go_fwd == 1)
      printf("PRIMER_LEFT%s_TM=%.3f\n", suffix, fwd->temp);
    if (go_rev == 1)
      printf("PRIMER_RIGHT%s_TM=%.3f\n", suffix, rev->temp);
    if (go_int == 1)
      printf("PRIMER_%s%s_TM=%.3f\n", int_oligo, suffix, intl->temp);

    /* Print fraction bound at melting temperature */
    if ((pa->annealing_temp > 0.0) && (pa->salt_corrections != 2)) {
      if ((go_fwd == 1) && (fwd->bound > 0.0))
        printf("PRIMER_LEFT%s_BOUND=%.3f\n", suffix, fwd->bound);
      if ((go_rev == 1) && (rev->bound > 0.0))
        printf("PRIMER_RIGHT%s_BOUND=%.3f\n", suffix, rev->bound);
      if ((go_int == 1) && (intl->bound > 0.0))
        printf("PRIMER_%s%s_BOUND=%.3f\n", int_oligo, suffix, intl->bound);
    }

    /* Print primer GC content */
    if (go_fwd == 1)
      printf("PRIMER_LEFT%s_GC_PERCENT=%.3f\n", suffix, fwd->gc_content);
    if (go_rev == 1)
      printf("PRIMER_RIGHT%s_GC_PERCENT=%.3f\n", suffix, rev->gc_content);
    if (go_int == 1)
      printf("PRIMER_%s%s_GC_PERCENT=%.3f\n", int_oligo, suffix,
             intl->gc_content);

    /* Print primer self_any*/
    if (go_fwd == 1 && pa->thermodynamic_oligo_alignment==0)
      printf("PRIMER_LEFT%s_SELF_ANY=%.2f\n", suffix, fwd->self_any);
    if (go_rev == 1 && pa->thermodynamic_oligo_alignment==0)
      printf("PRIMER_RIGHT%s_SELF_ANY=%.2f\n", suffix, rev->self_any);
    if (go_int == 1 && pa->thermodynamic_oligo_alignment==0)
      printf("PRIMER_%s%s_SELF_ANY=%.2f\n", int_oligo, suffix, intl->self_any);
    /* Print primer self_any thermodynamical approach */
    if (go_int == 1 && pa->thermodynamic_oligo_alignment==1)
      printf("PRIMER_%s%s_SELF_ANY_TH=%.2f\n", int_oligo, suffix, intl->self_any);
    if (go_fwd == 1 && pa->thermodynamic_oligo_alignment==1)
      printf("PRIMER_LEFT%s_SELF_ANY_TH=%.2f\n", suffix, fwd->self_any);
    if (go_rev == 1 && pa->thermodynamic_oligo_alignment==1)
      printf("PRIMER_RIGHT%s_SELF_ANY_TH=%.2f\n", suffix, rev->self_any);
    /* Print primer secondary structures*/
    if (go_fwd == 1 && pa->show_secondary_structure_alignment==1 && fwd->self_any_struct != NULL)
      printf("PRIMER_LEFT%s_SELF_ANY_STUCT=%s\n", suffix, fwd->self_any_struct);
    if (go_rev == 1 && pa->show_secondary_structure_alignment==1 && rev->self_any_struct != NULL)
      printf("PRIMER_RIGHT%s_SELF_ANY_STUCT=%s\n", suffix, rev->self_any_struct);
    if (go_int == 1 && pa->show_secondary_structure_alignment==1 && intl->self_any_struct != NULL)
      printf("PRIMER_%s%s_SELF_ANY_STUCT=%s\n", int_oligo, suffix, intl->self_any_struct);

    /* Print primer self_end and secondary structures*/
    if (go_fwd == 1 && pa->thermodynamic_oligo_alignment==0)
      printf("PRIMER_LEFT%s_SELF_END=%.2f\n", suffix, fwd->self_end);
    if (go_rev == 1 && pa->thermodynamic_oligo_alignment==0)
      printf("PRIMER_RIGHT%s_SELF_END=%.2f\n", suffix, rev->self_end);
    if (go_int == 1 && pa->thermodynamic_oligo_alignment==0)
      printf("PRIMER_%s%s_SELF_END=%.2f\n", int_oligo, suffix, intl->self_end);
    /* Print primer self_end thermodynamical approach */
    if (go_int == 1 && pa->thermodynamic_oligo_alignment==1)
      printf("PRIMER_%s%s_SELF_END_TH=%.2f\n", int_oligo, suffix, intl->self_end);
    if (go_fwd == 1 && pa->thermodynamic_oligo_alignment==1)
      printf("PRIMER_LEFT%s_SELF_END_TH=%.2f\n", suffix, fwd->self_end);
    if (go_rev == 1 && pa->thermodynamic_oligo_alignment==1)
      printf("PRIMER_RIGHT%s_SELF_END_TH=%.2f\n", suffix, rev->self_end);
    /* Print primer secondary structures*/
    if (go_fwd == 1 && pa->show_secondary_structure_alignment==1 && fwd->self_end_struct != NULL)
      printf("PRIMER_LEFT%s_SELF_END_STUCT=%s\n", suffix, fwd->self_end_struct);
    if (go_rev == 1 && pa->show_secondary_structure_alignment==1 && rev->self_end_struct != NULL)
      printf("PRIMER_RIGHT%s_SELF_END_STUCT=%s\n", suffix, rev->self_end_struct);
    if (go_int == 1 && pa->show_secondary_structure_alignment==1 && intl->self_end_struct != NULL)
      printf("PRIMER_%s%s_SELF_END_STUCT=%s\n", int_oligo, suffix, intl->self_end_struct);

    /* Print primer hairpin */
    if (go_fwd == 1 && pa->thermodynamic_oligo_alignment==1)
      printf("PRIMER_LEFT%s_HAIRPIN_TH=%.2f\n", suffix, fwd->hairpin_th);
    if (go_rev == 1 && pa->thermodynamic_oligo_alignment==1)
      printf("PRIMER_RIGHT%s_HAIRPIN_TH=%.2f\n", suffix, rev->hairpin_th);
    if (go_int == 1 && pa->thermodynamic_oligo_alignment==1)
      printf("PRIMER_%s%s_HAIRPIN_TH=%.2f\n", int_oligo, suffix, intl->hairpin_th);
    /* Print primer secondary structures*/
    if (go_fwd == 1 && pa->show_secondary_structure_alignment==1 && fwd->hairpin_struct != NULL)
      printf("PRIMER_LEFT%s_HAIRPIN_STUCT=%s\n", suffix, fwd->hairpin_struct);
    if (go_rev == 1 && pa->show_secondary_structure_alignment==1 && rev->hairpin_struct != NULL)
      printf("PRIMER_RIGHT%s_HAIRPIN_STUCT=%s\n", suffix, rev->hairpin_struct);
    if (go_int == 1 && pa->show_secondary_structure_alignment==1 && intl->hairpin_struct != NULL)
      printf("PRIMER_%s%s_HAIRPIN_STUCT=%s\n", int_oligo, suffix, intl->hairpin_struct);

    /*Print out primer mispriming scores */
     if (seq_lib_num_seq(pa->p_args.repeat_lib) > 0) {
       if (go_fwd == 1)
         printf("PRIMER_LEFT%s_LIBRARY_MISPRIMING=%.2f, %s\n", suffix,
                fwd->repeat_sim.score[fwd->repeat_sim.max],
                fwd->repeat_sim.name);
       if (go_rev == 1)
         printf("PRIMER_RIGHT%s_LIBRARY_MISPRIMING=%.2f, %s\n", suffix,
                rev->repeat_sim.score[rev->repeat_sim.max],
                rev->repeat_sim.name);
       if (retval->output_type == primer_pairs)
         printf("PRIMER_PAIR%s_LIBRARY_MISPRIMING=%.2f, %s\n", suffix,
                retval->best_pairs.pairs[i].repeat_sim,
                retval->best_pairs.pairs[i].rep_name);
     }
                
     /* Print out internal oligo mispriming scores */
     if (go_int == 1 && seq_lib_num_seq(pa->o_args.repeat_lib) > 0)
       printf("PRIMER_%s%s_LIBRARY_MISHYB=%.2f, %s\n", int_oligo, suffix,
              intl->repeat_sim.score[intl->repeat_sim.max],
              intl->repeat_sim.name);

    /* If a sequence quality was provided, print it*/
    if (NULL != sa->quality){
      if (go_fwd == 1)
        printf("PRIMER_LEFT%s_MIN_SEQ_QUALITY=%d\n", suffix,
               fwd->seq_quality);
      if (go_rev == 1) 
        printf("PRIMER_RIGHT%s_MIN_SEQ_QUALITY=%d\n", suffix,
               rev->seq_quality);
      if (go_int == 1 && (retval->output_type == primer_list)) 
        printf("PRIMER_%s%s_MIN_SEQ_QUALITY=%d\n", int_oligo, suffix,
               intl->seq_quality);
      /* Has to be here and in primer pairs for backward compatibility */
    }
        
    /* Print position penalty, this is for backward compatibility */
    if (!_PR_DEFAULT_POSITION_PENALTIES(pa)
        || !PR_START_CODON_POS_IS_NULL(sa)){
      printf("PRIMER_LEFT%s_POSITION_PENALTY=%f\n", suffix,
             fwd->position_penalty);
      printf("PRIMER_RIGHT%s_POSITION_PENALTY=%f\n", suffix,
             rev->position_penalty);
    }
        
    /* Print primer end stability */
    if (go_fwd == 1)
      printf("PRIMER_LEFT%s_END_STABILITY=%.4f\n",
             suffix, fwd->end_stability);
    if (go_rev == 1)
      printf("PRIMER_RIGHT%s_END_STABILITY=%.4f\n",
             suffix, rev->end_stability);

    /* Print primer template mispriming */
    if ( (pa->thermodynamic_template_alignment == 0) && (go_fwd == 1) && 
         (oligo_max_template_mispriming(fwd) != ALIGN_SCORE_UNDEF))
      printf("PRIMER_LEFT%s_TEMPLATE_MISPRIMING=%.4f\n", suffix,
             oligo_max_template_mispriming(fwd));
    if ( (pa->thermodynamic_template_alignment == 0) && (go_rev == 1) && 
         (oligo_max_template_mispriming(rev) != ALIGN_SCORE_UNDEF))
      printf("PRIMER_RIGHT%s_TEMPLATE_MISPRIMING=%.4f\n", suffix,
             oligo_max_template_mispriming(rev));

    /* Print primer template mispriming, thermodynamical approach*/
    if ( (pa->thermodynamic_template_alignment == 1) && (go_fwd == 1) &&
         (oligo_max_template_mispriming_thermod(fwd) != ALIGN_SCORE_UNDEF)) {
      printf("PRIMER_LEFT%s_TEMPLATE_MISPRIMING_TH=%.4f\n", suffix,
             oligo_max_template_mispriming_thermod(fwd));
    }
    if ( (pa->thermodynamic_template_alignment == 1) && (go_rev == 1) &&
         (oligo_max_template_mispriming_thermod(rev) != ALIGN_SCORE_UNDEF)) {
      printf("PRIMER_RIGHT%s_TEMPLATE_MISPRIMING_TH=%.4f\n", suffix,
             oligo_max_template_mispriming_thermod(rev));
    /* Print primer secondary structures*/
    if ((go_fwd == 1) && (pa->show_secondary_structure_alignment==1) && 
        (oligo_max_template_mispriming_struct(fwd) != NULL))
      printf("PRIMER_LEFT%s_TEMPLATE_MISPRIMING_STUCT=%s\n", suffix, 
             oligo_max_template_mispriming_struct(fwd));
    if ((go_rev == 1) && (pa->show_secondary_structure_alignment==1) &&
        (oligo_max_template_mispriming_struct(rev) != NULL))
      printf("PRIMER_RIGHT%s_TEMPLATE_MISPRIMING_STUCT=%s\n", suffix, 
             oligo_max_template_mispriming_struct(rev));

#if 0
       printf("DEBUG_PRIMER_RIGHT%s_TEMPLATE_MISPRIMING_TOP_TH=%.4f\n", suffix,
              rev->template_mispriming);
       printf("DEBUG_PRIMER_RIGHT%s_TEMPLATE_MISPRIMING_R_TH=%.4f\n", suffix,
              rev->template_mispriming_r);
#endif
     }
     /************************************************************************************/
     /* Print the pair parameters*/
    if (retval->output_type == primer_pairs) {
      if (go_int == 1 && NULL != sa->quality) /* FIX ME - Uptate the tests */
        printf("PRIMER_%s%s_MIN_SEQ_QUALITY=%d\n", int_oligo,
               suffix, intl->seq_quality);
      /* Print pair comp_any */
       if(pa->thermodynamic_oligo_alignment==0)
         printf("PRIMER_PAIR%s_COMPL_ANY=%.2f\n", suffix,
                retval->best_pairs.pairs[i].compl_any);
       if(pa->thermodynamic_oligo_alignment==1)
         printf("PRIMER_PAIR%s_COMPL_ANY_TH=%.2f\n", suffix,
                retval->best_pairs.pairs[i].compl_any);
       /* Print primer secondary structures*/
       if (pa->show_secondary_structure_alignment==1 && 
           retval->best_pairs.pairs[i].compl_any_struct != NULL)
         printf("PRIMER_PAIR%s_COMPL_ANY_STUCT=%s\n", suffix, 
                retval->best_pairs.pairs[i].compl_any_struct);
       /* Print pair comp_end */
       if(pa->thermodynamic_oligo_alignment==0)
         printf("PRIMER_PAIR%s_COMPL_END=%.2f\n", suffix,
                retval->best_pairs.pairs[i].compl_end);
       if(pa->thermodynamic_oligo_alignment==1)
         printf("PRIMER_PAIR%s_COMPL_END_TH=%.2f\n", suffix,
                retval->best_pairs.pairs[i].compl_end);
       /* Print primer secondary structures*/
       if (pa->show_secondary_structure_alignment==1 && 
           retval->best_pairs.pairs[i].compl_end_struct != NULL)
         printf("PRIMER_PAIR%s_COMPL_END_STUCT=%s\n", suffix, 
                retval->best_pairs.pairs[i].compl_end_struct);
       /* Print product size */
       int product_size = retval->best_pairs.pairs[i].product_size;
       if (NULL != sa->overhang_left) {
         product_size += strlen(sa->overhang_left);
       }
       if (NULL != sa->overhang_right) {
         product_size += strlen(sa->overhang_right);
       }
       printf("PRIMER_PAIR%s_PRODUCT_SIZE=%d\n", suffix,
              product_size);
       /* Print the product Tm if a Tm range is defined */
       if (pa->product_max_tm != PR_DEFAULT_PRODUCT_MAX_TM ||
           pa->product_min_tm != PR_DEFAULT_PRODUCT_MIN_TM) {
         printf("PRIMER_PAIR%s_PRODUCT_TM=%.4f\n", suffix,
                retval->best_pairs.pairs[i].product_tm);
         
         printf("PRIMER_PAIR%s_PRODUCT_TM_OLIGO_TM_DIFF=%.4f\n", suffix,
                retval->best_pairs.pairs[i].product_tm_oligo_tm_diff);
         
         printf("PRIMER_PAIR%s_T_OPT_A=%.4f\n", suffix,
                retval->best_pairs.pairs[i].t_opt_a);
       } else {
         printf("PRIMER_PAIR%s_PRODUCT_TM=%.1f\n", suffix,
                retval->best_pairs.pairs[i].product_tm);
       }
      
      /* Print the primer pair template mispriming */
      if ((pa->thermodynamic_template_alignment == 0) && (retval->best_pairs.pairs[i].template_mispriming != ALIGN_SCORE_UNDEF))
        printf("PRIMER_PAIR%s_TEMPLATE_MISPRIMING=%.2f\n", suffix,
               retval->best_pairs.pairs[i].template_mispriming);
       /* Print the primer pair template mispriming. Thermodynamic approach.  */
       if ((pa->thermodynamic_template_alignment == 1) && (retval->best_pairs.pairs[i].template_mispriming != ALIGN_SCORE_UNDEF))
         printf("PRIMER_PAIR%s_TEMPLATE_MISPRIMING_TH=%.2f\n", suffix,
                retval->best_pairs.pairs[i].template_mispriming);
       /* Print primer secondary structures*/
       if (pa->show_secondary_structure_alignment==1 &&
           retval->best_pairs.pairs[i].template_mispriming_struct != NULL)
         printf("PRIMER_PAIR%s_TEMPLATE_MISPRIMING_STUCT=%s\n", suffix,
                retval->best_pairs.pairs[i].template_mispriming_struct);

    } /* End of print parameters of primer pairs */
        
  } /* End of the big loop printing all data */
    
    /* End the print with newline and flush all buffers */
  printf("=\n");
  if (fflush(stdout) == EOF) {
    perror("fflush(stdout) failed");
    exit(-1);
  }
}

void
print_boulder_error(const char *err) 
{
  printf("PRIMER_ERROR=%s\n=\n", err);
  if (fflush(stdout) == EOF) {
    perror("fflush(stdout) failed");
    exit(-1);
  }
}

void
print_boulder_warning(const char *err) 
{
  printf("PRIMER_WARNING=%s\n", err);
  if (fflush(stdout) == EOF) {
    perror("fflush(stdout) failed");
    exit(-1);
  }
}

static void
print_all_explain(const p3_global_settings *pa,
                  const seq_args *sa, 
                  const p3retval *retval,
                  int io_version)
{
  if (pa->pick_left_primer == 1
      && !(pa->pick_anyway && sa->left_input))
    printf("PRIMER_LEFT_EXPLAIN=%s\n",
           p3_get_oligo_array_explain_string(p3_get_rv_fwd(retval)));

  if (pa->pick_right_primer == 1
      && !(pa->pick_anyway && sa->right_input))
    printf("PRIMER_RIGHT_EXPLAIN=%s\n",
           p3_get_oligo_array_explain_string(p3_get_rv_rev(retval)));

  if ( pa->pick_internal_oligo == 1
      && !(pa->pick_anyway && sa->internal_input)) 
    printf("PRIMER_INTERNAL_EXPLAIN=%s\n",
           p3_get_oligo_array_explain_string(p3_get_rv_intl(retval)));

  if (pa->pick_right_primer == 1 
      && pa->pick_left_primer == 1) {
    printf("PRIMER_PAIR_EXPLAIN=%s\n", 
           p3_get_pair_array_explain_string(p3_get_rv_best_pairs(retval)));
  }
}
