
/**
 * projectM -- Milkdrop-esque visualisation SDK
 * Copyright (C)2003-2007 projectM Team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * See 'LICENSE.txt' included within this release
 *
 */
/**
 * $Id$
 *
 * MilkdropPreset
 *
 * $Log$
 */

#ifndef _MilkdropPreset_HPP
#define _MilkdropPreset_HPP

#include "Common.hpp"
#include <string>
#include <cassert>
#include <map>

#ifdef DEBUG
  /* 0 for no debugging, 1 for normal, 2 for insane */
  #define MILKDROP_PRESET_DEBUG 1
#else
  #define MILKDROP_PRESET_DEBUG 0
#endif

#include "CustomShape.hpp"
#include "CustomWave.hpp"
#include "Expr.hpp"
#include "PerPixelEqn.hpp"
#include "PerFrameEqn.hpp"
#include "BuiltinParams.hpp"
#include "PresetFrameIO.hpp"
#include "InitCond.hpp"
#include "Preset.hpp"

class MilkdropPresetFactory;
class CustomWave;
class CustomShape;
class InitCond;


class MilkdropPreset : public Preset
{

public:


  ///  Load a MilkdropPreset by filename with input and output buffers specified.
  /// \param absoluteFilePath the absolute file path of a MilkdropPreset to load from the file system
  /// \param milkdropPresetName a descriptive name for the MilkdropPreset. Usually just the file name
  /// \param presetOutputs initialized and filled with data parsed from a MilkdropPreset
  MilkdropPreset(MilkdropPresetFactory *factory, const std::string & absoluteFilePath, const std::string & milkdropPresetName, PresetOutputs & presetOutputs);

  ///  Load a MilkdropPreset from an input stream with input and output buffers specified.
  /// \param in an already initialized input stream to read the MilkdropPreset file from
  /// \param milkdropPresetName a descriptive name for the MilkdropPreset. Usually just the file name
  /// \param presetOutputs initialized and filled with data parsed from a MilkdropPreset
  MilkdropPreset(MilkdropPresetFactory *factory, std::istream & in, const std::string & milkdropPresetName, PresetOutputs & presetOutputs);

  ~MilkdropPreset();

  /// All "builtin" parameters for this MilkdropPreset. Anything *but* user defined parameters and
  /// custom waves / shapes objects go here.
  /// @bug encapsulate
  BuiltinParams builtinParams;


  /// Used by parser to find/create custom waves and shapes. May be refactored
  template <class CustomObject>
  static CustomObject * find_custom_object(int id, std::vector<CustomObject*> & customObjects);


  int per_pixel_eqn_string_index;
  int per_frame_eqn_string_index;
  int per_frame_init_eqn_string_index;

  int per_frame_eqn_count,
  per_frame_init_eqn_count;


  /// Used by parser
  /// @bug refactor
  int add_per_pixel_eqn( char *name, Expr *gen_expr );

  /// Accessor method to retrieve the absolute file path of the loaded MilkdropPreset
  /// \returns a file path string
  std::string absoluteFilePath() const
  {
    return _absoluteFilePath;
  }


  /// Accessor method for the MilkdropPreset outputs instance associated with this MilkdropPreset
  /// \returns A MilkdropPreset output instance with values computed from most recent evaluateFrame()
  PresetOutputs & presetOutputs() const
  {
    return _presetOutputs;
  }

  const PresetInputs & presetInputs() const
  {
    return _presetInputs;
  }


// @bug encapsulate

  PresetOutputs::cwave_container customWaves;
  PresetOutputs::cshape_container customShapes;

  /// @bug encapsulate
  /* Data structures that contain equation and initial condition information */
  std::vector<PerFrameEqn*>  per_frame_eqn_tree;   /* per frame equations */
  std::map<int, PerPixelEqn*>  per_pixel_eqn_tree; /* per pixel equation tree */
  Expr *per_pixel_program;
  std::map<std::string,InitCond*>  per_frame_init_eqn_tree; /* per frame initial equations */
  std::map<std::string,InitCond*>  init_cond_tree; /* initial conditions */
  std::map<std::string,Param*> user_param_tree; /* user parameter splay tree */


  PresetOutputs & pipeline() { return _presetOutputs; } 

  void Render(const BeatDetect &music, const PipelineContext &context);
  const std::string & name() const;
  const std::string & filename() const { return _filename; } 
private:
  std::string _filename; 
  PresetInputs _presetInputs;
  /// Evaluates the MilkdropPreset for a frame given the current values of MilkdropPreset inputs / outputs
  /// All calculated values are stored in the associated MilkdropPreset outputs instance
  void evaluateFrame();

  // The absolute file path of the MilkdropPreset
  std::string _absoluteFilePath;

  // The absolute path of the MilkdropPreset
  std::string _absolutePath;

  void initialize(const std::string & pathname);
  void initialize(std::istream & in);

  int loadPresetFile(const std::string & pathname);

  void loadBuiltinParamsUnspecInitConds();
  void loadCustomWaveUnspecInitConds();
  void loadCustomShapeUnspecInitConds();

  void evalCustomWavePerFrameEquations();
  void evalCustomShapePerFrameEquations();
  void evalPerFrameInitEquations();
  void evalCustomWaveInitConditions();
  void evalCustomShapeInitConditions();
  void evalPerPixelEqns();
  void evalPerFrameEquations();
  void initialize_PerPixelMeshes();
  int readIn(std::istream & fs);

  void preloadInitialize();
  void postloadInitialize();

  MilkdropPresetFactory *_factory;
  PresetOutputs & _presetOutputs;

template <class CustomObject>
void transfer_q_variables(std::vector<CustomObject*> & customObjects);

friend class MilkdropPresetFactory;
};


template <class CustomObject>
void MilkdropPreset::transfer_q_variables(std::vector<CustomObject*> & customObjects)
{
 	CustomObject * custom_object;

	for (typename std::vector<CustomObject*>::iterator pos = customObjects.begin(); pos != customObjects.end();++pos) {

		custom_object = *pos;
		for (unsigned int i = 0; i < NUM_Q_VARIABLES; i++)
			custom_object->q[i] = _presetOutputs.q[i];
	}


}

template <class CustomObject>
CustomObject * MilkdropPreset::find_custom_object(int id, std::vector<CustomObject*> & customObjects)
{

  CustomObject * custom_object = NULL;


  for (typename std::vector<CustomObject*>::iterator pos = customObjects.begin(); pos != customObjects.end();++pos) {
	if ((*pos)->id == id) {
		custom_object = *pos;
		break;
	}
  }

  if (custom_object == NULL)
  {

    if ((custom_object = new CustomObject(id)) == NULL)
    {
      return NULL;
    }

      customObjects.push_back(custom_object);

  }

  assert(custom_object);
  return custom_object;
}



#endif /** !_MilkdropPreset_HPP */
