@*
POSIXGenerator.tmpl

Created by Graham Dennis on 2010-11-28.

Copyright (c) 2010-2012, Graham Dennis

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 2 of the License, or
(at your option) any later version.

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, see <http://www.gnu.org/licenses/>.

*@
@extends xpdeint.ScriptElement
@from xpdeint.PrintfSafeFilter import PrintfSafeFilter
@from xpdeint.CallOnceGuards import callOnceGuard

@def seedCount
  @return len($seedArray) or 10
@end def

@def globals
  @#
uint32_t ${generatorName}_seeds[${seedCount}];
  @#
@end def

@def initialiseGlobalSeeds
  @#
  @set $featureOrdering = ['Driver']
  @#
  @if not $seedArray
${seedSystemRandomNumberGenerator}@slurp
    @#
    @set $seedGenerationDict = {'extraIndent': 0}
${insertCodeForFeatures('runtimeSeedGenerationBegin', $featureOrdering, $seedGenerationDict)}@slurp
    @silent extraIndent = seedGenerationDict['extraIndent']
${insertCodeForFeaturesInReverseOrder('runtimeSeedGenerationEnd', $featureOrdering, $seedGenerationDict)}@slurp
  @else
    @for seedIdx, seed in enumerate($seedArray)
${generatorName}_seeds[$seedIdx] = $seed;
    @end for
  @end if
  @#
@end def

@# We only need to seed the system random number generator once, even if there are multiple Generator objects.
@@callOnceGuard
@def seedSystemRandomNumberGenerator
#if HAVE_DEV_URANDOM
  uint32_t __seeds[10];
  FILE *__urandom_fp = fopen("/dev/urandom", "r");

  if (__urandom_fp == NULL) {
      _LOG(_ERROR_LOG_LEVEL, "Unable to seed random number generator from /dev/urandom.  Is it accessible?\n");
      // Implicit quit
  }

  size_t __entries_read = 0;
  __entries_read = fread(__seeds, sizeof(uint32_t), 10, __urandom_fp);

  if (__entries_read != 10) {
    _LOG(_ERROR_LOG_LEVEL, "Unable to read from /dev/urandom while seeding the random number generator.\n");
      // Implicit quit
  }

  fclose(__urandom_fp);

  for (unsigned long _i0=0; _i0 < ${seedCount}; _i0++) {
    ${generatorName}_seeds[_i0] = (uint32_t) __seeds[_i0];
  }

#else
#error Do not have a run-time random number source! Please supply seeds manually.
#endif
@end def

@def initialiseLocalSeeds
  @#
  @set $featureOrdering = ['Driver']
  @silent seedOffset = $insertCodeForFeatures('seedOffset', $featureOrdering)
  @#
uint32_t ${generatorName}_local_seeds[${seedCount}] = {
  ${',\n  '.join([c'${generatorName}_seeds[$i]+(0${seedOffset})*${i+1}' for i in xrange($seedCount)])}
};
  @#
@end def

@def xsilOutputInfo($dict)
@*doc:
Write to the XSIL file lines naming the seeds that we generated if no seed was provided
in the script file. These are the seeds that should be provided in the script file to get
the same results.
*@
  @#
  @set $fp = dict['fp']
  @#
  @if len($seedArray)
    @return
  @end if
  @#
fprintf($fp, "\nNo seeds were provided for noise vector '${parent.parent.name}'. The seeds generated were:\n");
fprintf($fp, "    ${', '.join(['%u' for _ in xrange($seedCount)])}\n", ${', '.join([c'${generatorName}_seeds[$i]' for i in xrange($seedCount)])});
@end def
