/*  lil-gp Genetic Programming System, version 1.0, 11 July 1995
 *  Copyright (C) 1995  Michigan State University
 * 
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of version 2 of the GNU General Public License as
 *  published by the Free Software Foundation.
 * 
 *  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.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *  
 *  Douglas Zongker       (zongker@isl.cps.msu.edu)
 *  Dr. Bill Punch        (punch@isl.cps.msu.edu)
 *
 *  Computer Science Department
 *  A-714 Wells Hall
 *  Michigan State University
 *  East Lansing, Michigan  48824
 *  USA
 *  
 */

#include <lilgp.h>

typedef struct
{
    char* sname;
    sel_context* sc;
} reproduce_data;

/* operator_reproduce_init()
 *
 * called to parse reproduction options and initialize one record of a
 * breedphase table.
 */

int operator_reproduce_init(char* options, breedphase* bp)
{
    int errors = 0;
    reproduce_data* rd;
    int i, j;
    char** argv;
    
    rd = (reproduce_data*) MALLOC(sizeof(reproduce_data));
    
    /* fill in the breedphase record. */
    bp->gp_operator = OPERATOR_REPRODUCE;
    bp->data = (void*) rd;
    bp->operator_free = operator_reproduce_free;
    bp->operator_start = operator_reproduce_start;
    bp->operator_end = operator_reproduce_end;
    bp->operator_operate = operator_reproduce;
    
    rd->sname = NULL;
    
    j = parse_o_rama(options, &argv);
    
    for (i = 0; i < j; ++i)
    {
        /* parse "select" option. */
        if (strcmp("select", argv[i]) == 0)
        {
            if (!exists_select_method(argv[++i]))
            {
                ++errors;
                error(E_ERROR, "reproduction: \"%s\" is not a known selection method.",
                      argv[i]);
            }
            FREE(rd->sname);
            rd->sname = (char*) MALLOC((strlen(argv[i]) + 1) * sizeof(char));
            strcpy(rd->sname, argv[i]);
        } else
        {
            ++errors;
            error(E_ERROR, "reproduction: unknown option \"%s\".",
                  argv[i]);
        }
    }
    
    free_o_rama(j, &argv);
    
    if (rd->sname == NULL)
    {
        ++errors;
        error(E_ERROR, "reproduction: no selection method specified.");
    }

#ifdef DEBUG
    if ( !errors )
    {
         printf ( "reproduction options:\n" );
         printf ( "   selection: %s\n", rd->sname==NULL?"NULL":rd->sname );
    }
#endif
    
    return errors;
}

/* operator_reproduce_free()
 *
 * frees the reproduction-specific data of a breedphase record.
 */

void operator_reproduce_free(void* data)
{
    reproduce_data* rd;
    
    rd = (reproduce_data*) data;
    
    FREE(rd->sname);
    FREE(rd);
}

/* operator_reproduce_start()
 *
 * gets the selection context for this phase. 
 */

void operator_reproduce_start(population* oldpop, void* data)
{
    reproduce_data* rd;
    select_context_func_ptr select_con;
    
    rd = (reproduce_data*) data;
    
    select_con = get_select_context(rd->sname);
    rd->sc = select_con(SELECT_INIT, NULL, oldpop, rd->sname);
}

/* operator_reproduce_end()
 *
 * frees the selection context for this phase.
 */

void operator_reproduce_end(void* data)
{
    reproduce_data* rd;
    
    rd = (reproduce_data*) data;
    rd->sc->context_method(SELECT_CLEAN, rd->sc, NULL, NULL);
}


/* operator_reproduce()
 *
 * does the reproduction operation.
 */

void operator_reproduce(population* oldpop, population* newpop,
                        void* data)
{
    int j;
    reproduce_data* rd = (reproduce_data*) data;
    
    /* select an individual... */
    j = rd->sc->select_method(rd->sc);
    
    /* ...and reproduce it into the new population. */
    duplicate_individual((newpop->ind) + newpop->next, (oldpop->ind) + j);
    newpop->ind[newpop->next].flags = FLAG_NONE;
    ++newpop->next;
}

