#!/usr/bin/env python
# Copyright (c) 2011-2012, Universite de Versailles St-Quentin-en-Yvelines
#
# This file is part of ASK.  ASK 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, version 2.
#
# 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., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

"""
This module draws random points
"""

from random import randint, random

from common.configuration import Configuration
from common.util import fatal


def draw_point(factors):
    """
    Selects a random point that satisfies the factors constraints.
    Each axis value is selected using a uniform distribution.
    """
    p = []
    for f in factors:
        if f["type"] == "integer":
            g = randint(int(f["range"]["min"]),
                        int(f["range"]["max"]))
            p.append(g)
        elif f["type"] == "float":
            g = (random() *
                (f["range"]["max"] - f["range"]["min"]) +
                f["range"]["min"])
            p.append(g)
        elif f["type"] == "categorical":
            g = randint(0, len(f["values"]) - 1)
            p.append(g)
        else:
            fatal("Wrong factor type: <{0}>"
                  .format(f["type"]))
    return tuple(p)


def generate_possible_points(factors, labelled, n):
    """
    Prepare a list of generators of all possible
    factors satisfying constraints
    """
    count = 0
    tries = 0

    # If no new point is available after MAX_TRIES, give up
    MAX_TRIES = 50
    while(count < n):
        p = draw_point(factors)
        if p in labelled and tries < MAX_TRIES:
            tries += 1
            continue
        else:
            tries = 0
            count += 1
            yield p


def randomsample(configuration, output_file, n):
    already_labelled = set()

    # open a file to write suggested points
    of = open(output_file, "w")
    for p in generate_possible_points(configuration["factors"],
                                      already_labelled,
                                      n):
        already_labelled.add(p)
        of.write(" ".join(map(str, p)) + "\n")
    of.close()


if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(
        description="Draws unique random points")
    parser.add_argument('configuration')
    parser.add_argument('output_file')
    args = parser.parse_args()
    conf = Configuration(args.configuration)
    if "seed" in conf("modules.bootstrap.params"):
        random.seed(conf("modules.bootstrap.params.seed", int))
    randomsample(
        conf,
        args.output_file,
        conf("modules.bootstrap.params.n", int))
